abstract class Logger {
    public static readonly INFO: number = 1;
    public static readonly DEBUG: number = 2;
    public static readonly ERROR: number = 3;
    protected level: number = 0;
    protected nextLogger: Logger | null = null;
    public setNext(logger: Logger): void {
        this.nextLogger = logger;
    }
    public log(level: number, message: string): void {
        if (this.level <= level) {
            this.write(message);
            return
        }
        if (this.nextLogger !== null) {
            this.nextLogger.log(level, message);
        }
    }
    protected abstract write(message: string): void;
}

class ConsoleLogger extends Logger {
    public constructor(level: number) {
        super();
        this.level = level;
    }
    protected override write(message: string): void {
        console.log(message);
    }
}

class ErrorLogger extends Logger {
    public constructor(level: number) {
        super();
        this.level = level;
    }
    protected override write(message: string): void {
        console.log(`Error: ${message}`);
    }
}

class FileLogger extends Logger {
    public constructor(level: number) {
        super();
        this.level = level;
    }
    protected override write(message: string): void {
        console.log(`Writing to file: ${message}`);
    }
}

function getChainOfLoggers(): Logger {
    const errorLogger: Logger = new ErrorLogger(Logger.ERROR);
    const consoleLogger: Logger = new ConsoleLogger(Logger.INFO);
    const fileLogger: Logger = new FileLogger(Logger.DEBUG);
    errorLogger.setNext(fileLogger);
    fileLogger.setNext(consoleLogger);
    return errorLogger;
}

function main() {
    const loggerChain: Logger = getChainOfLoggers();
    loggerChain.log(Logger.INFO, "This is an info message");
    loggerChain.log(Logger.DEBUG, "This is a debug message");
    loggerChain.log(Logger.ERROR, "This is an error message");
}

main();
